<template>
  <!-- 输入框 -->
  <div class="box">
    <div class="page_left" :style="headerStyle">
      <div class="left_box" :style="boderStyle">
        <div class="talk_img">
          <img class="img" src="../../../assets/images/cpecc.png" alt="" />
        </div>

        <div class="talk" :class="{ 'talk-active': activeTab === '1' }" @click="setActiveTab('1')">
          <svg-icon icon-class="aitalk" class="icon_talk"></svg-icon>
          <p>问答</p>
        </div>

        <div class="talk" :class="{ 'talk-active': activeTab === '2' }" @click="setActiveTab('2')">
          <svg-icon icon-class="ai_file" class="icon_talk"></svg-icon>
          <p>文档</p>
        </div>
      </div>

      <div class="right_box" v-if="activeTab === '1'">
        <div class="new_chat" @click="newChat">
          <el-icon class="icon_new"><Plus /></el-icon>
          <span class="txt">发起新对话</span>
        </div>

        <!-- 中间的滚动区域 -->
        <div class="history_container">
          <div class="history_chat" v-for="(v, i) in history" :key="i" :class="{ 'activeSelected': selectedIndex == i }" @click="selectHistory(i)">
            <span>{{ v && v.name ? (v.name.length > 7 ? v.name.slice(0, 7) + '...' : v.name) : '' }}</span>
            <div class="icons">
              <el-tooltip content="查看文件" effect="customized" placement="top">
                <el-icon class="icons_file" @click="ClickFile(v)">
                  <Paperclip />
                </el-icon>
              </el-tooltip>
              <div style="margin: 0 5px"></div>

              <el-tooltip content="删除记录" effect="customized" placement="top">
                <el-icon class="icons_del" @click="delHistory(v)"><Delete /></el-icon>
              </el-tooltip>
            </div>
          </div>
        </div>

        <div class="clear_history">
          <el-icon class="icon_clear"><Delete /></el-icon>
          <span class="clear_txt">清空历史记录</span>
        </div>
      </div>

      <!-- 文档预览的位置 -->
      <div class="preview" v-if="activeTab === '2'">
        <iframe :src="onlineSrc" scrolling="auto" frameborder="0" class="iframeClass"></iframe>
      </div>
    </div>

    <!-- 输入框 -->
    <div class="input-bot" :style="inputStyle">
      <div class="input_tab">
        <div class="talk-tab" :class="{ 'activeTab': activeInput === '1' }" @click="InputTab('1')">
          <svg-icon icon-class="ai_essay" class="size_icon"></svg-icon>问答
        </div>
        <div class="talk-tab" :class="{ 'activeTab': activeInput === '2' }" @click="InputTab('2')">
          <svg-icon icon-class="ai_summary" class="size_icon"></svg-icon>总结
        </div>
        <div class="talk-tab" :class="{ 'activeTab': activeInput === '3' }" @click="InputTab('3')">
          <svg-icon icon-class="ai_translate" class="size_icon"></svg-icon>翻译
        </div>

        <div class="talk-tab" :class="{ 'activeTab': activeInput === '4' }" @click="InputTab('4')">
          <svg-icon icon-class="ai_mindMap" class="size_icon"></svg-icon>脑图
        </div>
      </div>
      <!-- 内容展示区域 -->
      <transition name="fade" @before-enter="beforeEnter" @enter="enter" @leave="leave">
        <div class="page_File" v-if="Listfile.length > 0 || inputMsg !== '' || messages.length > 0">
          <div class="chat-container">
            <div class="chat-messages" ref="chatMessages" v-if="messages.length">
              <div v-for="(message, index) in messages" :key="index">
                <!-- 如果是用户消息 -->
                <div v-if="message.isUser" class="user-message">
                  <div class="messageisUser">
                    <div class="message-content user">
                      <span>{{ message.content }}</span>
                    </div>
                    <img class="avatarUser" :src="userStore.avatar" alt="暂无" />
                  </div>
                </div>

                <!-- 如果是AI（或其他）消息 -->
                <div v-else class="ai-message">
                  <div class="message-content-wrapper">
                    <svg-icon icon-class="ai_chat2" class="AIavatar"></svg-icon>
                    <div class="message-content other">
                      <!-- 如果是 loading 状态 -->
                      <span v-if="message.isLoading" class="loading-text">加载中...</span>
                      <!-- 如果不是 loading，显示实际消息 -->
                      <span v-else style="letter-spacing: 2px">{{ message.content }}</span>
                    </div>
                  </div>
                </div>
              </div>
            </div>

            <div class="chat-messages" ref="chatMessages" v-if="!messages.length">
              <el-empty description="暂无问答内容" />
            </div>
          </div>
        </div>
      </transition>

      <!-- 输入框部分 -->
      <div class="page_input">
        <div class="input_box">
          <el-input
            ref="textInputRefs"
            v-model="inputMsg"
            :autosize="{ minRows: 3, maxRows: 5 }"
            type="textarea"
            resize="none"
            placeholder="请输入内容"
            @keyup.ctrl.enter.native="handleKeyPress"
            @keydown.enter.exact.native="enterSubmit"
            @input="sendTextInput"
          ></el-input>

          <div class="btn_box">
            <div class="voice_box" title="语音输入" v-if="activeTab === '1'">
              <img
                v-if="isSpeakType == 1"
                src="../../../assets/images/chatgpt/mic.png"
                style="width: 20px; height: 20px"
                alt=""
                @click="startRecordAudio"
              />
              <div v-else-if="isSpeakType == 2">
                <div class="loaderRectangle" @click="stopRecordAudio">
                  <div></div>
                  <div></div>
                  <div></div>
                  <div></div>
                  <div></div>
                </div>
              </div>

              <div v-else-if="isSpeakType == 3" class="SpeakType3">
                <i class="el-icon-loading"></i>
                <span>转码中...</span>
              </div>
            </div>

            <div class="voice_file" title="上传文件" v-if="activeTab === '2'">
              <el-upload
                accept=".docx,doc,.xlsx,.pptx"
                class="upload-demo"
                :auto-upload="false"
                action="/"
                :on-change="handlePreview"
                :on-remove="handleRemove"
                :before-remove="beforeRemove"
                multiple
                :limit="1"
                :on-exceed="handleExceed"
                :file-list="Listfile"
                ref="uploadRef"
                :show-file-list="false"
              >
                <el-icon style="font-size: 23px">
                  <Paperclip />
                </el-icon>
              </el-upload>
            </div>

            <el-divider direction="vertical" />
            <!-- 发送按钮 -->
            <div class="sendBtn" title="发送" @click="sendText">
              <svg-icon icon-class="send1"></svg-icon>
            </div>
          </div>
        </div>
      </div>

      <!-- 文件展示部分 -->
      <div class="file-list" v-if="Listfile.length > 0">
        <div class="file-item" slot="file" :slot-scope="{ filedow }">
          <svg-icon icon-class="fork" class="fork-icon" @click="forkRemove(filedow)"></svg-icon>
          <div class="file-box">
            <div class="file-icon">
              <svg-icon :icon-class="selectSvgIcons(filedow)" class="icon_file"></svg-icon>
            </div>

            <div class="file-name">
              <span class="file_name">{{ filedow.fileName }} </span>
              <span class="file-size">{{ formatSize(filedow.size) }}</span>

              <!-- <span></span> -->
            </div>
          </div>

          <!-- <div class="file-size">{{ formatFileSize(filedow.size) }}</div> -->
        </div>
      </div>
      <div class="tips">服务生成的所有内容均由人工智能模型生成，其生成内容的准确性和完整性无法保证，不代表我们的态度或观点</div>
    </div>
  </div>

  <!-- 输入框 -->
  <!-- <div class="input_box_input">
    <el-input
      type="textarea"
      id="textareaMsg"
      ref="textInput"
      :autosize="{ minRows: 1, maxRows: 12 }"
      class="textarea textInput1"
      resize="none"
      :placeholder="inputPlaceholder + `${$t('chatGpt.CtrlEnter')}`"
      v-model="inputMsg"
      @keydown.native="handleKeyPress"
      @keyup.enter.native="sendText"
      @input="sendTextInput"
      @blur="sendTextBlur"
      @focus="sendTextFocus"
    ></el-input>

    <div class="send_btn_box">
      <div class="voice_box">
        <el-icon style="font-size: 23px"><Paperclip /></el-icon>
      </div>
      <div class="send_btn" @click="sendText">{{ $t('chatGpt.sendBtn') }}</div>
    </div>
  </div> -->
</template>

<script setup lang="ts">
import { getToken } from '@/utils/auth';
import { ApiAIDocumentary } from '@/api/personDoc/aiApi';
import { defineProps, onMounted, ref, reactive } from 'vue';

const { proxy } = getCurrentInstance() as any;
/***********************************ai文档 start */
const isContextShow = ref(false); //语境中心消息显示
const isSpeakType = ref(1); //1:静默状态,还未点击开始录音,2:开始录音,3:结束录音,正在转换中
const chatListId = ref(''); //聊天记录的id
const chatList = ref([]); //聊天列表

const inputMsg = ref('');
const showHight = ref(false);
const isQa = ref(false);

/*********************************增加部分start */
const selectedIndex = ref(null); // 用来记录当前选中的历史记录项
const onlineSrc = ref('');
// 点击历史记录的颜色选中
const selectHistory = (index) => {
  selectedIndex.value = index; // 记录点击的历史记录项
};

const headerStyle = ref({
  width: '300px' // 初始高度
});

const boderStyle = ref({
  borderRight: '1px solid #efc486'
});

const inputStyle = ref({
  width: '900px'
});

const activeInput = ref('1');
const activeTab = ref('1');
const history = ref([
  { id: '1', name: '历史记录' },
  { id: '3', name: '历史记录发发范德萨范德萨发' },
  { id: '2', name: '测试记录' }
]);
import axios from 'axios';
import Recorder from 'js-audio-recorder';
// 定义Recorder对象
const recorder: any = new Recorder({
  sampleBits: 16, // 采样位数，支持 8 或 16，默认是16
  sampleRate: 16000, // 采样率，支持 11025、16000、22050、24000、44100、48000，根据浏览器默认值，我的chrome是48000
  numChannels: 1 // 声道，支持 1 或 2， 默认是1
  // compiling: false,(0.x版本中生效,1.x增加中)  // 是否边录边转换，默认是false
});

const textInputRefs = ref();

// 切换选中的 tab
const setActiveTab = (tab) => {
  activeTab.value = tab;
  if (tab == '1') {
    headerStyle.value.width = '300px';
    Listfile.value = [];
    filedow.value = '';
    boderStyle.value.borderRight = '1px solid #efc486';
  } else {
    headerStyle.value.width = '70px';
    boderStyle.value.borderRight = 'none';
    selectedIndex.value = null;
  }
};
// 头部
const InputTab = (tab) => {
  activeInput.value = tab;
};

// 展示文件
const ClickFile = (val) => {
  console.log('🚀 ~ ClickFile ~ val:', val);
};

// 开始录音
const startRecordAudio = () => {
  //@ts-ignore
  Recorder?.getPermission().then(
    () => {
      // console.log("开始录音");
      recorder.start(); // 开始录音
      inputMsg.value = '';
      isSpeakType.value = 2;
    },
    (error: any) => {
      proxy.$modal.msgWarning('请先允许该网页使用麦克风');
      // console.log(`${error.name} : ${error.message}`);
    }
  );
};

// 停止录音
const stopRecordAudio = () => {
  recorder.stop();
  var wavBlob = recorder.getWAVBlob();
  isSpeakType.value = 3;
  var formData = new FormData();
  const newbolb = new Blob([wavBlob], { type: 'audio/wav' });
  // //获取当时时间戳作为文件名
  const fileOfBlob = new File([newbolb], new Date().getTime() + '.wav');
  formData.append('file', fileOfBlob);
  axios
    .post('/stt', formData, {
      headers: {
        'Content-Type': 'multipart/form-data'
      }
    })
    .then((res) => {
      console.log(res);
      inputMsg.value = res.data;
      isSpeakType.value = 1;
      textInputRefs.value.focus();
    })
    .catch((err) => {
      isSpeakType.value = 1;
      proxy.$modal.msgError('语音识别失败，请重试');
    });
};

//新建对话
const newChat = () => {
  inputMsg.value = '';
  activeTab.value = '1';
  Listfile.value = [];
  filedow.value = '';
};
// 删除对话记录
const delHistory = (val) => {
  console.log('🚀 ~ delHistory ~ val:', val);
};
/*********************************增加部分end */

/*******************************************ai方法start */
const chatMessages = ref(null);
const messages = ref([]) as any;
import useUserStore from '@/store/modules/user';
const userStore = useUserStore();
/**input事件-------------------------start */
// 键盘按下事件--阻止回车换行,执行ctrl + enter换行
const handleKeyPress = (event: any) => {
  // console.log('🚀 ~ handleKeyPress ~ event:', event);
  if (event.keyCode == 13) {
    if (!event.ctrlKey) {
      event.preventDefault();
    } else {
      inputMsg.value += '\n';
    }
  }
};

const formatSize = (size: number) => {
  return proxy.$yuUtils.filterSizeUnit(size);
};
const enterSubmit = (e) => {
  e.preventDefault();
  sendText();
};
const sendTextInput = (e) => {
  if (e.trim() !== '') {
    showHight.value = true;
  }
};

/**input事件-------------------------end */

// 移除文件
const forkRemove = (file) => {
  // 调用 `ElMessageBox` 弹出确认框
  ElMessageBox.confirm(`确定移除 ${file.name} ?`, '确认移除', {
    confirmButtonText: '确认',
    cancelButtonText: '取消',
    type: 'warning'
  })
    .then(() => {
      // 这里调用 `handleRemove` 来移除文件
      const index = Listfile.value.findIndex((f) => f.name === file.name);
      if (index !== -1) {
        Listfile.value.splice(index, 1); // 移除文件
        isQa.value = false;
      }
      console.log('🚀 ~ forkRemo ~ file removed:', file);
    })
    .catch(() => {
      console.log('🚀 ~ forkRemo ~ file removal cancelled:', file);
    });
};
const TranslateInfo = ref('');

const sendText00 = async () => {
  if (inputMsg.value.trim() === '' && Listfile.value.length === 0) {
    proxy?.$modal.msgWarning(proxy.$t('chatGpt.chatMsg1'));
    return;
  }

  let formdata = new FormData();
  formdata.append('file', files.value);
  formdata.append('content', inputMsg.value);

  // 先立刻将用户消息添加到消息列表
  if (inputMsg.value.trim()) {
    messages.value.push({
      content: inputMsg.value,
      isUser: true
    });

    // 滚动到底部
    nextTick(() => {
      scrollToBottom();
    });

    inputMsg.value = ''; // 清空输入框
  }

  try {
    // 获取AI总结的响应
    const res = (await ApiAIDocumentary(formdata)) as any;
    console.log('🚀 ~ sendText ~ res:', res);

    // 过滤并拼接非 "data:" 字段的内容
    const filteredResult = res
      .split('data:') // 按 'data:' 分割字符串
      .filter((item) => item.trim() !== '') // 过滤空字符串
      .map((item) => item.trim()) // 移除两边的空格
      .join(' '); // 将过滤后的部分重新拼接成一个字符串

    TranslateInfo.value = filteredResult;

    // 模拟其他用户的回复
    setTimeout(() => {
      messages.value.push({
        content: TranslateInfo.value,
        isUser: false
      });
      nextTick(() => {
        scrollToBottom();
      });
    }, 1000);
  } catch (error) {
    // 发生错误时停止加载
    console.error('Error while processing the AI response:', error);
  }
};

//接口发送消息
const sendText0 = async () => {
  if (inputMsg.value.trim() === '') {
    // 如果消息为空且文件列表为空，则弹出警告
    proxy?.$modal.msgWarning(proxy.$t('chatGpt.chatMsg1'));
    return;
  } else if (Listfile.value.length == 0) {
    ElMessage.warning('请选择上传文件');
    return;
  } else {
    let formdata = new FormData();
    formdata.append('file', files.value);
    formdata.append('content', inputMsg.value);

    // 1. 整理消息列表
    // 对 messages.value 进行处理，整理成 API 所需的格式
    const history = messages.value
      .filter((message) => !message.isLoading) // 跳过正在加载的消息
      .map((message) => ({
        role: message.isUser ? 'user' : 'assistant', // 根据 isUser 字段设置角色
        content: message.content
      }));

    formdata.append('history', JSON.stringify(history)); // 将整理后的历史消息转为 JSON 字符串

    // 2. 立即将用户消息添加到消息列表
    if (inputMsg.value.trim()) {
      messages.value.push({
        content: inputMsg.value,
        isUser: true
      });

      // 滚动到底部
      nextTick(() => {
        scrollToBottom();
      });

      inputMsg.value = ''; // 清空输入框

      // 3. 添加 AI 的 loading 消息（此时 AI 消息还未返回）
      messages.value.push({
        content: '...',
        isUser: false,
        isLoading: true // 标记为 loading 状态
      });

      // 滚动到底部
      nextTick(() => {
        scrollToBottom();
      });
    }

    try {
      // 获取 AI 总结的响应
      const res = (await ApiAIDocumentary(formdata)) as any;

      // 过滤并拼接非 "data:" 字段的内容
      const filteredResult = res
        .split('data:') // 按 'data:' 分割字符串
        .filter((item) => item.trim() !== '') // 过滤空字符串
        .map((item) => item.trim()) // 移除两边的空格
        .join(' '); // 将过滤后的部分重新拼接成一个字符串

      TranslateInfo.value = filteredResult;

      // 4. 获取到 AI 回复后，找到 loading 消息并替换为 AI 回复
      const loadingIndex = messages.value.findIndex((message) => message.isLoading);
      if (loadingIndex !== -1) {
        // 替换掉 loading 消息
        messages.value[loadingIndex] = {
          content: '',
          isUser: false,
          isLoading: false // 标记为非 loading
        };

        // 滚动到底部
        nextTick(() => {
          scrollToBottom();
        });

        // 5. 开始流式显示 AI 回复内容
        let charIndex = 0;
        const messageContent = TranslateInfo.value;

        const typeEffect = () => {
          if (charIndex < messageContent.length) {
            messages.value[loadingIndex].content += messageContent.charAt(charIndex);
            charIndex++;
            nextTick(() => {
              scrollToBottom();
            });
          } else {
            clearInterval(typeInterval); // 一旦显示完毕，停止 interval
          }
        };

        // 使用 setInterval 来模拟逐字显示的效果
        const typeInterval = setInterval(typeEffect, 50); // 每 50ms 显示一个字符
      }
    } catch (error) {
      // 发生错误时停止加载
      console.error('处理AI响应时出错:', error);
      // 如果有错误，可以将 loading 消息替换成错误提示
      const loadingIndex = messages.value.findIndex((message) => message.isLoading);
      if (loadingIndex !== -1) {
        messages.value[loadingIndex] = {
          content: '发生错误，请重试.',
          isUser: false,
          isLoading: false
        };
        nextTick(() => {
          scrollToBottom();
        });
      }
    }
  }
};

watch(isQa, (newValue, oldValue) => {
  // console.log('isQa has changed:', newValue, 'from', oldValue);
});

// 处理 API 响应的函数，解析流式数据
const handleApiResponse = async (response) => {
  const reader = response.body.getReader(); // 获取响应的 body 流
  const decoder = new TextDecoder(); // 创建一个解码器，用于将字节流转换为文本
  let accumulatedData = ''; // 用于累积收到的数据
  let isMessageStarted = false; // 标记是否开始接收有效的消息
  let isMessageEnded = false; // 标记消息是否结束

  // 循环读取流数据，直到完成
  while (true) {
    const { done, value } = await reader.read(); // 读取当前块的数据
    if (done) break; // 如果没有更多数据了，停止循环

    const text = decoder.decode(value, { stream: true }); // 解码流数据为文本

    // 检查是否是消息的开始标记
    if (text.includes('data:message_start')) {
      isMessageStarted = true; // 标记消息开始
      accumulatedData = ''; // 清空已累积的数据，重新开始
    }

    // 如果已经开始接收消息，则继续累积数据
    if (isMessageStarted) {
      accumulatedData += text; // 将解码后的文本累积起来

      // 如果遇到消息结束标记，则标记消息结束
      if (text.includes('data:message_end')) {
        isMessageEnded = true;
      }
    }

    // 只有当消息完整接收完毕后，才进行处理
    if (isMessageEnded) {
      // 处理接收到的流数据，去除不必要的标识符
      let filteredData = accumulatedData
        .split('data:') // 按 'data:' 分割字符串
        .filter((item, index) => {
          // 过滤掉第一个 'message_start' 和最后的 'message_end' 标识符
          return !((index === 0 && item === 'message_start') || item === 'message_end');
        })
        .map((item) => item) // 去除两端的空格
        .join('') // 合并有效的数据部分
        .replace(/message_start|message_end/g, ''); // 删除所有标识符

      // 判断并分别处理中文和英文的部分
      let finalData = '';

      // 按字符分割，分别处理中文和英文
      let tempData = '';
      for (let i = 0; i < filteredData.length; i++) {
        const char = filteredData[i];

        // 判断字符是否为英文或中文
        if (/[\u4e00-\u9fa5]/.test(char)) {
          // 中文字符：直接添加并去除空格
          if (tempData.trim() !== '') {
            finalData += tempData.replace(/\s+/g, ''); // 中文部分去空格
            tempData = '';
          }
          finalData += char; // 添加中文字符
        } else if (/[a-zA-Z0-9]/.test(char)) {
          // 英文字符：保留空格
          tempData += char;
        } else {
          // 其他字符（如标点等），根据需求决定是否保留
          tempData += char;
        }
      }

      // 如果 tempData 中有剩余英文内容，添加到最终数据中
      if (tempData.trim() !== '') {
        finalData += tempData;
      }

      // 将处理后的文本赋值给 TranslateInfo
      TranslateInfo.value = finalData;

      // 找到正在加载的消息并替换为 AI 回复
      const loadingIndex = messages.value.findIndex((message) => message.isLoading);
      if (loadingIndex !== -1) {
        messages.value[loadingIndex] = { content: '', isUser: false, isLoading: false }; // 替换为没有内容的非加载状态
        nextTick(() => scrollToBottom()); // 滚动到底部，确保显示更新

        // 开始逐字显示 AI 回复的内容
        let charIndex = 0; // 当前显示字符的索引
        const typeEffect = () => {
          if (charIndex < finalData.length) {
            messages.value[loadingIndex].content += finalData.charAt(charIndex); // 逐字显示
            charIndex++; // 更新字符索引
            nextTick(() => scrollToBottom()); // 滚动到底部
          } else {
            clearInterval(typeInterval); // 完成逐字显示后停止定时器
          }
        };

        // 使用 setInterval 模拟逐字显示效果
        const typeInterval = setInterval(typeEffect, 50); // 每 50ms 显示一个字符
      }

      // 处理完毕后重置标记
      isMessageStarted = false;
      isMessageEnded = false;
      accumulatedData = ''; // 清空累积的数据
    }
  }
};

// 发送消息(自定义axios发送)
const sendText = async () => {
  if (!isQa.value) {
    // 普通聊天模式
    if (inputMsg.value.trim() === '') {
      // 如果用户输入为空，弹出警告
      proxy?.$modal.msgWarning(proxy.$t('chatGpt.chatMsg1'));
      return;
    }

    // 整理历史消息，去除正在加载的消息
    const history = messages.value
      .filter((message) => !message.isLoading)
      .map((message) => ({ role: message.isUser ? 'user' : 'assistant', content: message.content }));

    // 构建请求参数
    const oParam = {
      content: inputMsg.value,
      history: JSON.stringify(history) || null
    };

    // 立即将用户消息和加载中的 AI 消息添加到消息列表
    addUserMessage(inputMsg.value);
    inputMsg.value = ''; // 清空输入框

    try {
      // 发送请求获取 AI 响应
      const response = await fetch(`${import.meta.env.VITE_APP_BASE_API}/knowledge/personal/chat/qa`, {
        method: 'POST',
        headers: {
          Authorization: 'Bearer ' + getToken(), // 使用 Bearer Token 认证
          'Content-Type': 'application/json', // 请求体为 JSON
          clientid: import.meta.env.VITE_APP_CLIENT_ID // 客户端 ID
        },
        body: JSON.stringify(oParam) // 请求体参数
      });
      // 处理 API 响应
      await handleApiResponse(response);
    } catch (error) {
      handleError(); // 发生错误时调用错误处理函数
    }
  } else {
    // 文件上传聊天模式
    if (inputMsg.value.trim() === '') {
      // 如果消息为空且文件未上传，弹出警告
      proxy?.$modal.msgWarning(proxy.$t('chatGpt.chatMsg1'));
      return;
    } else if (isQa.value && Listfile.value.length === 0) {
      // 如果选择了文件上传模式，但未选择文件，弹出提示
      ElMessage.warning('请选择上传文件');
      return;
    }

    // 创建 FormData 实例，用于上传文件和消息内容(表单传参)
    const formData = new FormData();
    formData.append('file', files.value); // 添加文件
    formData.append('content', inputMsg.value); // 添加消息内容

    // 整理历史消息，去除正在加载的消息
    const history = messages.value
      .filter((message) => !message.isLoading)
      .map((message) => ({ role: message.isUser ? 'user' : 'assistant', content: message.content }));

    formData.append('history', JSON.stringify(history)); // 将历史消息转换为 JSON 字符串并添加到 FormData

    // 立即将用户消息和加载中的 AI 消息添加到消息列表
    addUserMessage(inputMsg.value);
    inputMsg.value = ''; // 清空输入框

    try {
      // 发送文件上传请求
      const response = await fetch(`${import.meta.env.VITE_APP_BASE_API}/knowledge/personal/chat/documentary/qa`, {
        method: 'POST',
        headers: {
          Authorization: 'Bearer ' + getToken(), // 使用 Bearer Token 认证
          clientid: import.meta.env.VITE_APP_CLIENT_ID // 客户端 ID
        },
        body: formData // 请求体为 FormData
      });
      // 处理 API 响应
      await handleApiResponse(response);
    } catch (error) {
      handleError(); // 发生错误时调用错误处理函数
    }
  }
};

// 添加用户消息和加载中的 AI 消息到消息列表
const addUserMessage = (messageContent) => {
  messages.value.push({
    content: messageContent, // 添加用户消息内容
    isUser: true // 标记为用户消息
  });

  nextTick(() => scrollToBottom()); // 滚动到底部

  messages.value.push({
    content: '...', // 初始 loading 消息
    isUser: false, // 标记为 AI 消息
    isLoading: true // 设置为加载状态
  });

  nextTick(() => scrollToBottom()); // 滚动到底部
};

// 错误处理函数
const handleError = () => {
  // 查找正在加载的消息
  const loadingIndex = messages.value.findIndex((message) => message.isLoading);

  if (loadingIndex !== -1) {
    // 替换为错误提示消息
    messages.value[loadingIndex] = {
      content: '发生错误，请稍后再试。',
      isUser: false,
      isLoading: false // 标记为非加载状态
    };

    nextTick(() => scrollToBottom()); // 滚动到底部
  }

  ElMessage.error('发生错误，请稍后重试。'); // 弹出错误提示
};

// 流式数据会有被截断的情况，需要做一些容错处理
const chunkRef = ref();
const handleChunkData = (chunk: string) => {
  chunk = chunk.trim();
  // 如果存在上一个切片
  if (chunkRef.value) {
    chunk = chunkRef.value + chunk;
    chunkRef.value = '';
  }

  // 如果存在done,认为是完整切片且是最后一个切片
  if (chunk.includes('[DONE]')) {
    return chunk;
  }

  // 最后一个字符串不为}，则默认切片不完整，保存与下次拼接使用（这种方法不严谨，但已经能解决大部分场景的问题）
  if (chunk[chunk.length - 1] !== '}') {
    chunkRef.value = chunk;
  }
  return chunk;
};

// 滚动到底部
const scrollToBottom = () => {
  const chatMag = chatMessages.value;
  chatMag.scrollTop = chatMag.scrollHeight;
};

/*******************************************ai方法end */

/*****************************文件上传 start */
const Listfile = ref([]);
const filedow = ref('') as any;
//判断是否显示AI问答页面
const updateShowHight = () => {
  if (Listfile.value.length > 0 || inputMsg.value.trim() !== '' || messages.value.length > 0) {
    showHight.value = true;
  }
};
const handleRemove = (file, fileList) => {
  Listfile.value = fileList; // 更新文件列表
  updateShowHight(); // 更新 showHight 状态

  // console.log(file, fileList);
};

watch(
  () => Listfile.value,
  (newValue) => {
    updateShowHight(); // 在 watch 中直接更新 showHight
  },
  { deep: true, immediate: true }
);

const files = ref('');
// 获取文件列表信息
const handlePreview = (file, fileList) => {
  files.value = file.raw;
  // 提取文件扩展名
  const suffix = file.name.split('.').pop() || '';
  const fileName = file.name.split('.')[0];

  // 判断文件类型，根据扩展名确定
  const validTypes = ['ppt', 'pptx', 'excel', 'docx', 'doc', 'xlsx', 'xls'];
  const type = validTypes.includes(suffix.toLowerCase()) ? 'file' : '';

  Listfile.value = fileList;

  if (Listfile.value.length > 0) {
    isQa.value = true;
  } else {
    isQa.value = false;
  }

  // 添加新的属性到 filedow.value
  filedow.value = {
    ...file,
    type, // 添加type属性
    suffix, // 添加suffix属性
    fileName //截取后的文件名
  };
};

const handleExceed = (files, fileList) => {
  ElMessage.warning(`当前限制选择 1 个文件，本次选择了 ${files.length} 个文件，共选择了 ${files.length + fileList.length} 个文件`);
};

import type { UploadProps } from 'element-plus';

const beforeRemove: UploadProps['beforeRemove'] = (uploadFile) => {
  // 使用ElMessageBox.confirm来确认是否移除文件
  return ElMessageBox.confirm(`确定移除 ${uploadFile.name} ?`, '确认移除', {
    confirmButtonText: '确认',
    cancelButtonText: '取消',
    type: 'warning'
  })
    .then(() => {
      // 每次文件移除后更新 key，强制触发渲染和过渡
      return true; // 移除文件
    })
    .catch(() => {
      return false; // 不移除文件
    });
};

/*****************************文件上传 end */

// 切换选中的 tab
const clear = () => {
  Listfile.value = [];
  inputMsg.value = '';
  messages.value = [];
};

const toggleListfile = () => {
  Listfile.value = Listfile.value.length > 0 ? [] : ['file1', 'file2'];
  // 每次更新 Listfile 后，强制触发渲染并更新 key
};
const toggleInputMsg = () => {
  inputMsg.value = inputMsg.value !== '' ? '' : ' ';
};

const beforeEnter = (el) => {
  el.style.height = '0'; // 初始高度为 0
  el.style.transition = 'height 0.5s ease-in-out'; // 设置过渡
};

const enter = (el, done) => {
  el.offsetHeight; // 强制触发重新计算
  // el.style.height = 'calc(100vh - 365px)'; // 过渡到目标高度
  el.style.height = 'calc(100vh - 410px)'; // 过渡到目标高度
  done(); // 完成过渡
};

const leave = (el, done) => {
  el.style.height = '0'; // 离开时高度恢复为 0
  done(); // 完成过渡
};

//icon图标
const selectSvgIcons = (v: any) => {
  return proxy.$yuUtils.selectSvgIcon(v);
};

onMounted(() => {});

defineExpose({
  //调用组件需要导出 v-model的变量
  clear
});
</script>

<style>
.el-popper.is-customized {
  /* Set padding to ensure the height is 32px */
  padding: 6px 12px;
  background: linear-gradient(90deg, #f08f00, #f6c683);
  color: #fff;
}

.el-popper.is-customized .el-popper__arrow::before {
  background: linear-gradient(90deg, #f08f00, #f6c683);
  right: 0;
  color: #fff;
}
</style>

<style scoped lang="scss">
/******************************************  录音样式start */
.voice_box {
  height: 20px;
  cursor: pointer;

  .SpeakType3 {
    width: 60px;
    font-size: 12px;
    color: #b3b0c3;
  }
}
.loaderRectangle {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 0 3px;
}

.loaderRectangle div {
  width: 4px;
  height: 8px;
  animation: 0.9s ease-in-out infinite;
  background: #3662ec;
  box-shadow: 0 0 20px rgba(18, 31, 53, 0.3);
}

.loaderRectangle div:nth-child(1) {
  animation-name: rectangleOneAnim;
  animation-delay: 1s;
}

@keyframes rectangleOneAnim {
  0% {
    height: 8px;
  }

  40% {
    height: 15px;
  }

  100% {
    height: 8px;
  }
}

.loaderRectangle div:nth-child(2) {
  animation-name: rectangleTwoAnim;
  animation-delay: 1.1s;
}

@keyframes rectangleTwoAnim {
  0% {
    height: 8px;
  }

  40% {
    height: 20px;
  }

  100% {
    height: 8px;
  }
}

.loaderRectangle div:nth-child(3) {
  animation-name: rectangleThreeAnim;
  animation-delay: 1.2s;
}

@keyframes rectangleThreeAnim {
  0% {
    height: 8px;
  }

  40% {
    height: 25px;
  }

  100% {
    height: 8px;
  }
}

.loaderRectangle div:nth-child(4) {
  animation-name: rectangleFourAnim;
  animation-delay: 1.3s;
}

@keyframes rectangleFourAnim {
  0% {
    height: 8px;
  }

  40% {
    height: 20px;
  }

  100% {
    height: 8px;
  }
}

.loaderRectangle div:nth-child(5) {
  animation-name: rectangleFiveAnim;
  animation-delay: 1.4s;
}

@keyframes rectangleFiveAnim {
  0% {
    height: 8px;
  }

  40% {
    height: 15px;
  }

  100% {
    height: 8px;
  }
}

/******************************************  录音样式end */
.preview {
  width: calc(500px - 65px);
  height: calc(100vh - 57px);
  border: 1px solid red;

  .iframeClass {
    width: calc(500px - 65px);
    height: calc(100vh - 57px);
  }
}
.box {
  width: 100%;
  display: flex;
  position: relative;

  .page_left {
    margin-right: 10px;
    height: calc(100vh - 55px);
    // border: 1px solid red;
    width: 300px;
    display: flex;
    border-radius: 10px;
    background: #ffffff;
    position: absolute;
    top: -100px;

    .left_box {
      display: flex;
      flex-direction: column;
      align-items: center;
      padding: 5px;
      width: 65px;
      height: 100%;

      .talk-active {
        background: #f08f001f;
        color: #f08f00fc;
      }

      .talk_img {
        padding: 5px;
        width: 50px;
        height: 50px;
        margin-bottom: 10px;
        .img {
          width: 45px;
          height: 45px;
        }
      }

      .talk {
        padding: 5px;
        width: 50px;
        height: 50px;
        display: flex;
        flex-direction: column;
        justify-content: space-around;
        align-items: center;
        margin-bottom: 10px;
        cursor: pointer;
        border-radius: 8px;

        .icon_talk {
          margin-bottom: 3px;
          font-size: 25px;
        }
      }
    }

    .right_box {
      // border-radius: 10px;
      // background: #ffffff;
      width: calc(300px - 65px);
      // border: 1px solid brown;
      display: flex;
      flex-direction: column;
      height: 100%; /* 使容器占满父级高度 */
      padding: 10px;
      box-sizing: border-box;
    }

    .new_chat {
      margin-bottom: 10px;
      cursor: pointer;
      background: #f08f00;
      align-items: center;
      border-radius: 32px;
      display: flex;
      flex-shrink: 0; /* 防止头部在高度不足时被压缩 */
      font-size: 14px;
      font-weight: 400;
      height: 36px;
      justify-content: center;
      line-height: 24px;
      opacity: 1;
      transition: all 0.3s;
      color: #ffffff;
    }

    .activeSelected {
      background: #f08f001f; /* 选中时的背景色 */
      color: #f08f00fc; /* 选中时的字体颜色 */
    }

    .history_container {
      overflow-y: auto;
      flex-grow: 1; /* 使历史记录部分填充剩余空间 */
      //padding-right: 10px; /* 防止滚动条遮挡内容 */
      .history_chat {
        // color: #f08f00fc;
        // background: #f08f001f;
        align-items: center;
        border-radius: 12px;
        box-sizing: border-box;
        cursor: pointer;
        display: flex;
        flex-shrink: 0;
        font-size: 14px;
        height: 36px;
        justify-content: space-between;
        margin-bottom: 12px;
        outline: none;
        padding: 6px 16px;
        position: relative;
        transition: 0.2s ease;

        &:hover {
          color: #f08f00fc;
          background: #f08f001f;
          .icons_file {
            display: block;
          }
        }
      }

      .icons {
        display: flex;
      }

      .icons_file {
        display: none;
        font-size: 14px;
      }

      .icons_del {
        font-size: 14px;
      }
    }

    // .history_chat :hover .icons_file {
    //   display: block;
    // }

    /* 用于支持滚动的部分 */
    .right_box {
      display: flex;
      flex-direction: column;
      height: 100%;
    }

    .clear_history {
      color: #fff;
      display: flex;
      align-items: center;
      justify-content: center;
      margin-top: auto; /* 确保尾部固定在底部 */
      cursor: pointer;
      background: #f08f00;
      border-radius: 32px;
      padding: 6px 16px;
      font-size: 14px;
      font-weight: 400;
      height: 36px;
      line-height: 24px;
      opacity: 1;
      transition: all 0.3s;
      flex-shrink: 0; /* 防止尾部被压缩 */
      margin-top: 10px;

      .icon_clear {
        font-size: 15px;
      }

      .clear_txt {
        font-size: 15px;
        margin-left: 6px;
        letter-spacing: 1px;
        font-family: inherit;
      }
    }
  }
}
/* 聊天消息样式 */
.ai-message .message-content-wrapper {
  display: flex;
  align-items: center;
}

.ai-message .AIavatar {
  width: 30px;
  height: 30px;
  margin-right: 10px;
}

/* 用户消息样式 */
.user-message .message-content {
  background-color: #f1f1f1;
  color: #333;
}

.ai-message .message-content {
  background-color: #e0f7fa;
  color: #00796b;
}

/* loading 状态的样式 */
.ai-message .message-content-wrapper .message-content {
  color: #999;
  font-style: italic;
}

.loading-text {
  color: #888;
  font-style: italic;
  animation: loading 1s infinite;
}

@keyframes loading {
  0% {
    opacity: 0.5;
  }
  50% {
    opacity: 1;
  }
  100% {
    opacity: 0.5;
  }
}

.page_File {
  z-index: 999;
  width: 900px;
  //   margin: 0 auto;
  //   margin-right: 280px;
  background: #fff;
  overflow-x: auto;
  padding: 10px;
  height: 0; /* 默认高度为 0 */
  max-height: calc(100vh - 365px); /* 最大高度 */
  // max-height: calc(100vh - 365px); /* 最大高度 */
  transition: height 0.5s ease-in-out; /* 过渡效果 */
  box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
  margin-bottom: 10px;
  border-radius: 8px;

  /* 聊天容器 */
  .chat-container {
    display: flex;
    flex-direction: column;
    height: calc(82vh - 130px);
    /* 占用视口高度，减去头部/底部的空间 */
    background-color: #fff;
  }

  /* 聊天消息区域 */
  .chat-messages {
    flex: 1;
    overflow-y: auto;
    /* 超过内容时滚动 */
    // margin-bottom: 10px;
    background-color: #fff;

    // box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
    // padding: 10px;
    display: flex;
    flex-direction: column;
  }

  /* 用户消息的样式（右对齐） */
  .user-message {
    flex-direction: row-reverse;
    /* 用户消息右对齐 */
    justify-content: flex-end;
  }

  /* AI（或其他人的）消息样式（左对齐） */
  .ai-message {
    flex-direction: row;
    /* AI消息左对齐 */
    justify-content: flex-start;
  }

  .messageisUser {
    display: flex;
    // align-items: flex-start;
    align-items: center;
    justify-content: flex-end;
    margin-bottom: 10px;
    //max-width: 75%; /* 限制最大宽度 */
    // padding: 5px;
  }

  /* 消息内容样式 */
  .message-content {
    padding: 10px;
    border-radius: 10px;
    word-wrap: break-word;
    line-height: 1.4;
    max-width: 90%;
    /* 消息内容最大宽度 */
  }

  /* 用户的消息样式 */
  .message-content.user {
    background-color: #0084ff;
    color: white;
    align-self: flex-end;
    /* 用户消息靠右对齐 */
  }

  /* 其他用户或AI的消息样式 */
  .message-content.other {
    background-color: #e1e1e1;
    color: black;
    align-self: flex-start;
    /* AI消息靠左对齐 */
  }

  .avatarUser {
    width: 32px;
    height: 32px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #fff;
    font-size: 12px;
    background-color: #56abfb;
    margin-left: 8px;
    /* 为了给消息内容腾出空间 */
  }

  /* 每条消息 */
  .message-content-wrapper {
    display: flex;
    // align-items: flex-start;
    align-items: center;
    margin-bottom: 10px;
    //max-width: 75%; /* 限制最大宽度 */
    // padding: 5px;
  }

  .AIavatar {
    width: 32px;
    height: 32px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    margin-right: 8px;
    /* 为了给消息内容腾出空间 */
    border: 1px solid #56abfb;
    // background-color: #56abfb;
  }
}

/* 为了视觉效果可以在进入时加个渐变 */
.page_File-enter-active,
.page_File-leave-active {
  transition: height 0.5s ease-in-out;
}

.page_File-enter,
.page_File-leave-to {
  height: 0; /* 初始/结束高度 */
}

.icon_file {
  height: 100%;
  font-size: 49px;
  /* vertical-align: middle; */
  /* margin-right: 6px; */
  margin-left: -7px;
}

.file-list {
  //   margin-right: 280px;
  width: 100%;
  width: 900px;
  max-height: 260px;
  padding-bottom: 12px;

  border-radius: 6px;
  background-color: #f3f5f9;
  overflow-y: auto;
  padding: 10px;

  .file-item {
    padding: 8px;
    width: calc((100% - 16px) / 3);
    position: relative;
    height: 60px;
    background: #fff;
    border-color: #fff;
    border-radius: 8px;
    box-shadow: 0px 0px 15px rgba(0, 0, 0, 0.3);
  }

  .file-box {
    display: flex;

    .file-name {
      margin-left: 10px;
      display: flex;
      flex-direction: column;

      .file_name {
        margin: 0px;
        font-family: 'PingFang SC', 'Microsoft YaHei', SimHei;
        font-weight: 700;
        font-size: 14px;
        line-height: 20px;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
      }

      .file-size {
        margin: 0px;
        font-size: 13px;
        line-height: 24px;
        font-weight: 400;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
      }
    }
  }

  .file-icon {
    width: 35px;
    height: 45px;
  }

  .fork-icon {
    position: absolute;
    cursor: pointer;
    font-size: 14px;
    top: -6px;
    right: -2px;
  }
}

.input-bot {
  display: flex;
  flex-direction: column;
  align-items: center;
  width: calc(100vw - 300px);

  //   width: 800px;
  height: 100%;

  .input_tab {
    width: 100%;
    //width: 900px;
    // width: calc(100vw - 300px);
    display: flex;
    align-items: center;
    justify-content: space-between;
    height: 40px;
    padding: 8px;
    border-radius: 16px;
    // background: rgba(255, 255, 255, 0.4);
    background: #fff;
    margin-bottom: 8px;

    .talk-tab {
      width: 100%;
      display: flex;
      height: 100%;
      align-items: center;
      justify-content: center;
      padding: 0 18px;
      font-size: 15px;
      border-radius: 10px;
      cursor: pointer;
      letter-spacing: 1.5px;

      .size_icon {
        font-size: 19px;
        font-weight: 600;
        margin-right: 6px;
      }
    }
  }

  .activeTab {
    font-weight: 600;
    color: #f08f00fc;
    background: #f08f001f;
    box-shadow: 0 4px 16px 1px #0000000f;
  }

  .tips {
    color: #8f91a8;
    font-size: 10px;
    height: 12px;
    line-height: 12px;
    margin: 0;
    overflow: hidden;
    text-align: center;
    text-overflow: ellipsis;
    white-space: nowrap;
    width: 100%;
    flex-shrink: 0;
    margin: 8px 0;
  }
}

.page_input {
  width: 100%;
  width: 900px;
  // max-width: 760px;
  // max-height: 150px;
  overflow-y: auto;
  //   margin: 0 auto;
  //   margin-right: 280px;
  display: flex;
  justify-content: center;
  align-items: flex-end;

  .create_btn {
    margin-left: 8px;
    // margin-bottom: 8px;
  }

  .img2 {
    width: 16px;
    height: 16px;
  }

  .input_box {
    flex: 1;
    display: flex;
    align-items: flex-end;
    background-color: #ffffff;
    border-radius: 6px;
    // border: 1px solid #ffdeae;
    box-shadow: 0px 0px 8px 0px rgba(240, 143, 0, 0.06);
    overflow-y: auto;
    padding: 8px;
    margin: 0 auto;

    :deep(.el-textarea__inner) {
      box-shadow: none;
    }

    .btn_box {
      display: flex;
      align-items: center;
      margin-bottom: 5px;
    }
  }

  .voice_file {
    height: 20px;
  }

  .sendBtn {
    width: 28px;
    height: 28px;
    text-align: center;
    line-height: 28px;
    background-color: #f08f00;
    color: #ffffff;
    border-radius: 6px;
    // margin-left: 8px;
    font-size: 17px;
    cursor: pointer;
  }
}

.input_box_input {
  padding: 8px 10px;
  width: 100%;
  height: 100%;
  max-height: 73px;
  display: flex;
  border-radius: 10px;
  align-items: flex-end;
  background: #ffffff;
}

textarea::-webkit-scrollbar {
  width: 3px;
  /* 设置滚动条宽度 */
}

textarea::-webkit-scrollbar-thumb {
  background-color: rgb(66, 70, 86);
  /* 设置滚动条滑块的背景色 */
  border-radius: 50%;
  /* 设置滑块的圆角 */
}

.textarea {
  &:focus {
    outline: none;
  }
}

:deep(.textInput1) {
  width: 100%;
  height: 33px;

  &:deep(.el-textarea__inner) {
    border: 0;
    font-size: 15px;
  }
}

:deep(.el-textarea__inner) {
  box-shadow: none !important;
  border: 0;
}

.send_btn_box {
  display: flex;
  align-items: center;

  img {
    cursor: pointer;
  }
}

// .voice_box {
//   cursor: pointer;

//   .SpeakType3 {
//     width: 60px;
//     font-size: 12px;
//     color: #b3b0c3;
//   }
// }

.send_btn {
  width: 67px;
  padding: 8px 0;
  text-align: center;
  border-radius: 8px;
  background-color: #5e7afc;
  color: #ffffff;
  margin-left: 16px;
  cursor: pointer;
}

.search-active {
  font-weight: 600;
  color: #202020;
  background: #ffffff;
  box-shadow: 0 4px 16px 1px #0000000f;
}

.icon_size {
  font-weight: 600;
  margin-right: 6px;
}

.top {
  width: 260px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 58px;
  padding: 8px;
  margin-bottom: 20px;
  border-radius: 16px;
  background: rgba(255, 255, 255, 0.4);

  .top-text {
    display: flex;
    height: 100%;
    align-items: center;
    justify-content: center;
    padding: 0 18px;
    font-size: 15px;
    border-radius: 10px;
    cursor: pointer;
    letter-spacing: 1.5px;
  }
}

.search_box {
  width: 100%;
}

:deep(.input_search) {
  border: none;
  width: 100%;
}

:deep(.el-input__wrapper) {
  border: none;
  padding: 0px;
  box-shadow: none;
  border-radius: 10px;
}

:deep(.el-input__inner:hover),
:deep(.el-input__inner:focus) {
  border: 2px solid #4a9dec;
  box-shadow: 0px 0px 0px 7px rgb(74, 157, 236, 20%);
  background-color: white;
}

:deep(.el-input__prefix) {
  top: 2px;
}

:deep(.el-input-group__prepend) {
  border-top-left-radius: 20px;
  border-bottom-left-radius: 20px;
  background-color: #3d9cfb;
  border-color: #3d9cfb;
  color: #ffffff;
}

:deep(.el-input__inner) {
  // padding-top: 8px;
  // padding-bottom: 8px;
  // line-height: 40px;
  // height: 40px;
  // // border-radius: 16px;
  // border-top-right-radius: 20px;
  // border-bottom-right-radius: 20px;
  border: 2px solid transparent;
  outline: none;
  overflow: hidden;
  background-color: #ffffff;
  border-radius: 10px;
  transition: all 0.5s;
  // border: 2px solid #4a9dec;
  box-shadow: 0 0.4rem #dfd9d9;
  padding-left: 8px;
}

.searchInput_name {
  width: 100%;
  display: flex;
  align-items: center;

  div {
    // width: 10px;
    overflow: hidden;
    /*内容超出后隐藏*/
    text-overflow: ellipsis;
    /*超出内容显示为省略号*/
    white-space: nowrap;
    /*文本不进行换行*/
  }
}

.content_box {
  border-bottom: 1px solid #efefef;
}

.search_docContent {
  font-size: 12px;
  color: #939292;
  line-height: 22px;
}

.superiorsPath {
  font-size: 12px;
  color: #d6d6d6;
  line-height: 22px;
  text-align: right;
  overflow: hidden;
  /*内容超出后隐藏*/
  text-overflow: ellipsis;
  /*超出内容显示为省略号*/
  white-space: nowrap;
  /*文本不进行换行*/
}

.folder {
  position: relative;
  animation: float 1.8s infinite ease-in-out;
  transition: transform 0.3s ease;
  margin-bottom: 6px;
}

.folder:hover {
  transform: scale(1.05);
}

.folder .top {
  background: linear-gradient(135deg, #ff9a56, #ff6f56);
  width: 60px;
  height: 15px;
  border-radius: 12px 12px 0 0;
  box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
  position: relative;
  z-index: 2;
}

.folder .bottom {
  background: linear-gradient(135deg, #ffe563, #ffc663);
  width: 100px;
  height: 70px;
  box-shadow: 0 15px 30px rgba(0, 0, 0, 0.3);
  border-radius: 0 10px 10px 10px;
  position: relative;
  top: -10px;
}

@keyframes float {
  0% {
    transform: translateY(0px);
  }

  50% {
    transform: translateY(-12px);
  }

  100% {
    transform: translateY(0px);
  }
}

.page_tags {
  margin-top: 15px;

  .tagclass {
    margin-right: 10px;
    cursor: pointer;
  }
}
</style>
